package com.yx.changdao.common.utils;

import com.yx.changdao.common.annotation.Dict;
import com.yx.changdao.common.annotation.DictParam;
import com.yx.changdao.common.annotation.ExportField;
import com.yx.changdao.common.data.ExcelSheetItem;
import com.yx.changdao.common.data.ExcelVersion;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Description: 注解 导出工具类
 *
 * <p><br>
 * @Auther: Zhangjiashun
 * @create 2019/11/20 9:55
 */
public class ExportUtils {
    public static final Logger log = LoggerFactory.getLogger(ExportUtils.class);

    /**
     * 导出 Excel
     *
     * @param clazz      需要导出的元素类
     * @param response   相应
     * @param fileName   输出文件名
     * @param tableTitle excel表格标题 为null不添加表格标题。
     * @param dataList   数据源
     * @param <T>        类
     * @return 导出是否成功
     */
    public static <T> Boolean exportExcel(
            Class<T> clazz,
            HttpServletResponse response,
            String fileName,
            String tableTitle,
            List<T> dataList) {
        // 表头信息
        List<String> headers = new ArrayList<>();
        List<Field> fieldSortList = getFieldsWithIndex(clazz.getDeclaredFields());

        // 表的内容结构 包含多行数据
        List<List<Object>> col = new ArrayList<>();
        Map<Field, String> getMethodNames = getFieldStringMap(headers, fieldSortList);

        // 遍历数据
        if (dataList != null) {
            for (T t : dataList) {
                List<Object> row = new ArrayList<>();
                Object val;
                for (Field field : getMethodNames.keySet()) {
                    try {
                        val = clazz.getMethod(getMethodNames.get(field)).invoke(t);
                        field.setAccessible(true);
                        Dict dict = field.getAnnotation(Dict.class);
                        if (dict != null) {
                            // 将字典值赋值
                            DictParam[] dicts = dict.dicts();
                            for (DictParam dictParam : dicts) {
                                if (val.toString().equals(dictParam.key())) {
                                    val = dictParam.value();
                                }
                            }
                        }

                        ExportField exportField = field.getAnnotation(ExportField.class);
                        // 如果是日期类型转换
                        if (field.getType() == Date.class) {
                            SimpleDateFormat format = new SimpleDateFormat(exportField.dateFormat());
                            val = format.format(val);
                        }
                        row.add(val == null ? "" : val);
                    } catch (NoSuchMethodException
                            | IllegalAccessException
                            | InvocationTargetException ignored) {
                    }
                }
                col.add(row);
            }
        }

        ExcelSheetItem excelSheetItem = new ExcelSheetItem();
        excelSheetItem.setDataList(col);
        excelSheetItem.setHeaders(headers);
        excelSheetItem.setTitle(tableTitle);
        ArrayList<ExcelSheetItem> excelSheetItems = new ArrayList<>();
        excelSheetItems.add(excelSheetItem);
        ExportExcelUtils.excelResponseSetProperties(fileName, response);
        try {
            ExportExcelUtils.createWorkbookAtOutStream(
                    ExcelVersion.V2003, excelSheetItems, response.getOutputStream(), true);
//            ExportExcelUtils.createWorkbookAtDisk(ExcelVersion.V2003, excelSheetItems, "./test.xls");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 导出 CSV
     *
     * @param clazz    需要导出的元素类
     * @param response 响应
     * @param fileName 输出文件名
     * @param dataList 数据源
     * @param <T>      类
     * @return 导出是否成功
     */
    public static <T> Boolean exportCSV(
            Class<T> clazz, HttpServletResponse response, String fileName, List<T> dataList) {
        // 表头信息
        List<String> headers = new ArrayList<>();
        List<String> mapkey = new ArrayList<>();
        List<Map<String, Object>> data = new ArrayList<>();
        List<Field> fieldSortList = getFieldsWithIndex(clazz.getDeclaredFields());

        Map<Field, String> getMethodNames = getFieldStringMap(headers, fieldSortList);

        // 遍历数据
        if (dataList != null) {
            for (T t : dataList) {
                Object val;
                Map<String, Object> map = new HashMap<>();
                for (Field field : getMethodNames.keySet()) {
                    try {
                        val = clazz.getMethod(getMethodNames.get(field)).invoke(t);
                        field.setAccessible(true);
                        Dict dict = field.getAnnotation(Dict.class);
                        if (dict != null) {
                            // 将字典值赋值
                            DictParam[] dicts = dict.dicts();
                            for (DictParam dictParam : dicts) {
                                if (val.toString().equals(dictParam.key())) {
                                    val = dictParam.value();
                                }
                            }
                        }

                        ExportField exportField = field.getAnnotation(ExportField.class);
                        // 如果是日期类型转换
                        if (field.getType() == Date.class) {
                            SimpleDateFormat format = new SimpleDateFormat(exportField.dateFormat());
                            val = format.format(val);
                            map.put(field.getName(), val);
                        }
                        map.put(field.getName(), val == null ? "" : val);
                    } catch (NoSuchMethodException
                            | IllegalAccessException
                            | InvocationTargetException ignored) {
                    }
                }
                data.add(map);
            }
        }

        for (Field field : fieldSortList) {
            mapkey.add(field.getName());
        }

        return ExportCSVUtils.export(fileName, response, headers, mapkey, data);
    }


    /**
     * 按 ExportField 中index 进行排序
     *
     * @param declaredFields 类属性
     * @return 排序后的属性
     */
    private static List<Field> getFieldsWithIndex(Field[] declaredFields) {
        List<Field> fieldSortList = new ArrayList<>(Arrays.asList(declaredFields));
        List<Field> removeItem = new ArrayList<>();
        // 将没有 ExportField 注解的 属性排除
        fieldSortList.forEach(
                field -> {
                    if (field.getAnnotation(ExportField.class) == null) {
                        removeItem.add(field);
                    }
                });
        removeItem.forEach(fieldSortList::remove);

        // 将数据按索引排序
        fieldSortList.sort((field1, field2) -> {
            int diff =
                    field1.getAnnotation(ExportField.class).index()
                            - field2.getAnnotation(ExportField.class).index();
            if (diff > 0) {
                return 1;
            } else if (diff < 0) {
                return -1;
            }
            return 0;
        });
        return fieldSortList;
    }


    /**
     * 获得 属性名和对应的get方法
     *
     * @param headers       标题头
     * @param fieldSortList 排序后的属性列表
     * @return map
     */
    private static Map<Field, String> getFieldStringMap(List<String> headers, List<Field> fieldSortList) {
        Map<Field, String> getMethodNames = new LinkedHashMap<>();
        // 获取所有标注注解的get方法
        for (Field field : fieldSortList) {
            field.setAccessible(true);
            ExportField annotation = field.getAnnotation(ExportField.class);
            // 获取注解上的表头名
            if (annotation == null) {
                break;
            }
            String headerVal = annotation.value();
            if (StringUtils.isBlank(headerVal)) {
                // 没有设置默认取属性名
                headerVal = field.getName();
            }
            headers.add(headerVal);
            String getMethodName =
                    "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
            getMethodNames.put(field, getMethodName);
        }
        return getMethodNames;
    }
}
